package cn.yunbamboos.rest.service.core;

import cn.hutool.core.util.StrUtil;
import cn.yunbamboos.exception.AppException;
import cn.yunbamboos.rest.service.IBeanInvoke;
import cn.yunbamboos.rest.service.ICreateInDTO;
import cn.yunbamboos.rest.service.ICustomClassLoader;
import cn.yunbamboos.rest.service.dto.in.BaseInDTO;
import cn.yunbamboos.rest.service.dto.out.BaseOutDTO;
import cn.yunbamboos.rest.service.invoke.GenerateBeanInvoke;
import cn.yunbamboos.rest.service.invoke.GenerateCreateInDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;


public class CustomClassLoader extends ClassLoader implements ICustomClassLoader {

    private static final Logger logger = LoggerFactory.getLogger(CustomClassLoader.class);

    private final boolean enableFile;

    public CustomClassLoader(ClassLoader parent,boolean enableFile) {
        super(parent);
        this.enableFile = enableFile;
    }

    @Override
    public ICreateInDTO getCreateDTOInstance(Class<? extends BaseInDTO> inClass) {
        GenerateCreateInDTO generate = new GenerateCreateInDTO(inClass, enableFile);
        byte[] bytes = generate.generate();
        String name = generate.getPackagePath() + "/" + generate.getClassName();
        Class<?> clz = this.defineClass(name.replace("/","."), bytes, 0, bytes.length, null);
        try {
            Class<? extends ICreateInDTO> invokeClz = ClassHelper.asSubclass(ICreateInDTO.class, clz);
            return invokeClz.getDeclaredConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            logger.error(StrUtil.format("new instance error {} {}", name, bytes.length), e);
        }
        throw AppException.c("create instance error {}", name);
    }

    @Override
    public IBeanInvoke getBeanInvokeInstance(Object bean, Class<?> inter, String methodName, Class<? extends BaseInDTO> in, Class<? extends BaseOutDTO> out, String returnType) {
        GenerateBeanInvoke generate = new GenerateBeanInvoke(inter, methodName, in, out, returnType, enableFile);
        byte[] bytes = generate.generate();
        String name = generate.getPackagePath() + "/" + generate.getClassName();
        Class<?> clz = this.defineClass(name.replace("/","."), bytes, 0, bytes.length, null);
        try {
            Class<? extends IBeanInvoke> invokeClz = ClassHelper.asSubclass(IBeanInvoke.class, clz);
            return invokeClz.getDeclaredConstructor(Object.class).newInstance(bean);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            logger.error("new instance error {} {}", name, bytes.length);
        }
        throw AppException.c("create instance error {}", name);
    }
}
